﻿using System.Windows.Input;

using YourLastOptionsDialog.Controls;

namespace YourLastOptionsDialog.ViewModels.Options
{
    /// <summary>
    /// An option that represents a <see cref="string"/>.
    /// </summary>
    public class StringOption : Option
    {
        private InputScopeNameValue _inputScopeNameValue;

        private InputScope _inputScope;

        private IValidator _validator;

        /// <summary>
        /// Gets or sets the value of the wrapped underlying source.
        /// </summary>
        public string TextValue
        {
            get
            {
                return (string)GetAccessor();
            }

            set
            {
                // check if we should write back
                if (Validator.Validate(value) == ValidationResult.Success)
                {
                    SetAccessor(value);
                }
            }
        }

        /// <summary>
        /// Gets or sets the minimum allowed length of the value.
        /// This is used for validation if no custom validator has been configured.
        /// The default value is zero.
        /// </summary>
        public int MinLength
        {
            get
            {
                var stringValidator = Validator as StringValidator;
                if (stringValidator != null)
                {
                    return stringValidator.MinLength;
                }

                return 0;
            }

            set
            {
                var stringValidator = GetOrCreateStringValidator();
                if (stringValidator != null)
                {
                    stringValidator.MinLength = value;
                    RaisePropertyChanged("MinLength");
                }
            }
        }

        /// <summary>
        /// Gets or sets the maximum allowed length of the value.
        /// This is used for validation if no custom validator has been configured.
        /// The default value is <see cref="int.MaxValue"/>.
        /// </summary>
        public int MaxLength
        {
            get
            {
                var stringValidator = Validator as StringValidator;
                if (stringValidator != null)
                {
                    return stringValidator.MaxLength;
                }

                return int.MaxValue;
            }

            set
            {
                var stringValidator = GetOrCreateStringValidator();
                if (stringValidator != null)
                {
                    stringValidator.MaxLength = value;
                    RaisePropertyChanged("MaxLength");
                }
            }
        }

        /// <summary>
        /// Gets or sets the input scope name value that is used to create the <see cref="InputScope"/> to display a suitable SIP keyboard layout to the user.
        /// </summary>
        public InputScopeNameValue InputScopeNameValue
        {
            get
            {
                return _inputScopeNameValue;
            }

            set
            {
                if (_inputScopeNameValue != value)
                {
                    _inputScopeNameValue = value;
                    RaisePropertyChanged("InputScopeNameValue");
                }
            }
        }

        /// <summary>
        /// Gets the input scope used to display a suitable SIP keyboard layout to the user. 
        /// This value is determined by the value of the <see cref="InputScopeNameValue"/> property.
        /// </summary>
        public InputScope InputScope
        {
            get
            {
                if (_inputScope == null)
                {
                    var inputScope = new InputScope();
                    var inputScopeName = new InputScopeName();
                    inputScopeName.NameValue = InputScopeNameValue;
                    inputScope.Names.Add(inputScopeName);
                    _inputScope = inputScope;
                }

                return _inputScope;
            }
        }

        /// <summary>
        /// Gets or sets a regular expression that is used to validate the string entered by the user.
        /// </summary>
        public string RegularExpression
        {
            get
            {
                var stringValidator = Validator as StringValidator;
                if (stringValidator != null)
                {
                    return stringValidator.RegularExpression;
                }

                return null;
            }

            set
            {
                var stringValidator = GetOrCreateStringValidator();
                if (stringValidator != null)
                {
                    stringValidator.RegularExpression = value;
                    RaisePropertyChanged("RegularExpression");
                }
            }
        }

        /// <summary>
        /// Gets or sets the validation error message that is used if the value is outside the configured 
        /// minimum or maximum length, or if the value does not satisfy the given regular expression.
        /// </summary>
        public string ValidationErrorMessage
        {
            get
            {
                var stringValidator = Validator as StringValidator;
                if (stringValidator != null)
                {
                    return stringValidator.ValidationErrorMessage;
                }

                return null;
            }

            set
            {
                var stringValidator = GetOrCreateStringValidator();
                if (stringValidator != null)
                {
                    stringValidator.ValidationErrorMessage = value;
                    RaisePropertyChanged("ValidationErrorMessage");
                }
            }
        }

        /// <summary>
        /// Gets or sets the type of validator to use for validation. If a validator is 
        /// specified, then none of the other validation properties are in effect anymore. In particular,
        /// if a validator is configured, the values of <see cref="MinLength"/>, <see cref="MaxLength"/>,
        /// <see cref="RegularExpression"/> and <see cref="ValidationErrorMessage"/> are ignored and the 
        /// validator is instead used for all validation.
        /// </summary>
        public IValidator Validator
        {
            get
            {
                return _validator;
            }

            set
            {
                if (_validator != value)
                {
                    _validator = value;
                    RaisePropertyChanged("Validator");
                }
            }
        }

        private StringValidator GetOrCreateStringValidator()
        {
            var stringValidator = Validator as StringValidator;
            if (stringValidator == null)
            {
                // only create a new validator if there's no existing one of different type (which takes precendence)
                if (Validator == null)
                {
                    var validator = new StringValidator("^.*$", "Please enter a valid text.");
                    Validator = validator;
                    return validator;
                }
            }

            // => if there's already a validator other than a string validator, no validator is created (this is null)!
            return stringValidator;
        }
    }
}